
package com.neusoft.bizcore.activiti.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.transaction.Transactional;

import org.activiti.editor.language.json.converter.util.CollectionUtils;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.neusoft.bizcore.activiti.business.ActBusiness;
import com.neusoft.bizcore.activiti.constants.BizcoreActivitiConstants;
import com.neusoft.bizcore.activiti.converter.WorkOrderConverter;
import com.neusoft.bizcore.activiti.dto.ProcessInstanceDTO;
import com.neusoft.bizcore.activiti.dto.ProcessNodeDTO;
import com.neusoft.bizcore.activiti.dto.TaskDTO;
import com.neusoft.bizcore.activiti.dto.WorkorderDTO;
import com.neusoft.bizcore.activiti.enums.WorkOrderStatus;
import com.neusoft.bizcore.activiti.model.WorkOrder;
import com.neusoft.bizcore.activiti.repository.WorkOrderRepository;
import com.neusoft.bizcore.activiti.service.ISerialNumService;
import com.neusoft.bizcore.activiti.service.LockDataService;
import com.neusoft.bizcore.activiti.service.MoldDefinitionService;
import com.neusoft.bizcore.activiti.service.PendingDataService;
import com.neusoft.bizcore.activiti.service.ProcessFormService;
import com.neusoft.bizcore.activiti.service.ProcessInstanceService;
import com.neusoft.bizcore.activiti.service.WorkOrderService;
import com.neusoft.bizcore.activiti.user.ActivitiUserDetail;
import com.neusoft.bizcore.auth.common.bean.UserBean;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.bizcore.web.utils.SpringUtil;

@Service
public class WorkOrderServiceImpl implements WorkOrderService {

    @Autowired
    private WorkOrderRepository workOrderRepository;
    @Autowired
    private LockDataService lockDataService;
    @Autowired
    private WorkOrderConverter workOrderConverter;
    @Autowired
    private WorkOrderService workOrderService;
    @Autowired
    private ProcessInstanceService processInstanceService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private PendingDataService pendingDataService;
    //    @Autowired
    //    private ActBusiness actBusiness;
    @Autowired
    private MoldDefinitionService moldDefinitionService;
    @Autowired
    private MoldDefinitionService definitionService;
    @Autowired
    private ActivitiUserDetail userDetail;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private ProcessFormService processFormService;
    @Autowired
    private ISerialNumService serialNumService;

    @Override
    public Page<WorkorderDTO> todoWorkSearch(final Pageable pageable, final Searchable searchable,
            final UserBean user) {

        return this.workSearch(pageable, searchable, BizcoreActivitiConstants.TASK_TYPR_TODO, user);
    }

    @Override
    public long countTodoWorkSearch(final Searchable searchable,
            final UserBean user) {

        return this.countWorkSearch(searchable, BizcoreActivitiConstants.TASK_TYPR_TODO, user);
    }

    @Override
    public Page<WorkorderDTO> doneWorkSearch(final Pageable pageable, final Searchable searchable,
            final UserBean user) {

        return this.workSearch(pageable, searchable, BizcoreActivitiConstants.TASK_TYPR_DONE, user);
    }

    @Override
    public long countDoneWorkSearch(final Searchable searchable, final UserBean user) {
        return this.countWorkSearch(searchable, BizcoreActivitiConstants.TASK_TYPR_DONE, user);
    }

    @Override
    public Page<WorkorderDTO> myWorkSearch(final Pageable pageable, final Searchable searchable,
            final UserBean user) {
        return this.workSearch(pageable, searchable, BizcoreActivitiConstants.TASK_TYPR_ME, user);
    }

    @Override
    public long countMeWorkSearch(final Searchable searchable, final UserBean user) {
        return this.countWorkSearch(searchable, BizcoreActivitiConstants.TASK_TYPR_ME, user);
    }

    @Override
    public WorkorderDTO details(final String workorderNum) {

        final WorkOrder model = this.workOrderRepository.findByWorkOrderNum(workorderNum);

        if (null == model) {
            throw new UnitedException(BizcoreActivitiConstants.WORK_ORDER_EXIST);
        }

        return this.workOrderConverter.toDTO(model);
    }

    @Override
    public void revoke(final String workorderNum, final UserBean user) {

        final WorkOrder model = this.workOrderRepository.findByWorkOrderNum(workorderNum);

        if (null == model) {
            throw new UnitedException(BizcoreActivitiConstants.WORK_ORDER_EXIST);
        }

        final String instanceId = model.getInstanceId();

        if (instanceId == null) {
            throw new UnitedException(BizcoreActivitiConstants.PROCESS_EXIST);
        }

        //        final List<Task> tasks = this.taskService.createTaskQuery().processInstanceId(instanceId).list();

        //        tasks.forEach(t -> this.taskService.claim(t.getId(), user.getUsername()));

        // 修改状态
        this.workOrderService.updateStatus(workorderNum, WorkOrderStatus.revoked);

        // 删除流程
        this.runtimeService.deleteProcessInstance(instanceId, BizcoreActivitiConstants.PROCESS_REVOKE_REASON);

        // 解锁数据
        this.lockDataService.deleteByWorkNum(workorderNum);

        // 删除中间表
        this.pendingDataService.deleteByWorkNum(workorderNum);

        final Map<String, ActBusiness> business = SpringUtil.getApplicationContext().getBeansOfType(ActBusiness.class);
        business.values().forEach(busi -> {
            busi.revokeBusinessData(model.getType(), workorderNum);
        });
    }

    @Transactional
    @Override
    public void delete(final String workorderNum) {

        final WorkOrder model = this.workOrderRepository.findByWorkOrderNum(workorderNum);

        if (null == model) {
            throw new UnitedException(BizcoreActivitiConstants.WORK_ORDER_EXIST);
        }

        // 解锁数据
        this.lockDataService.deleteByWorkNum(workorderNum);
        // 删除业务数据
        final String formKey = this.moldDefinitionService.getStartForm(model.getType()).getFormKey();

        //this.actBusiness.deleteBusinessData(formKey, workorderNum);

        final Map<String, ActBusiness> business = SpringUtil.getApplicationContext().getBeansOfType(ActBusiness.class);
        business.values().forEach(busi -> {
            busi.deleteBusinessData(formKey, workorderNum);
        });
        // 删除工单表
        this.workOrderRepository.deleteById(model.getId());

    }

    @Override
    public String getInstanceIdByWorkorderNum(final String workorderNum) {
        String instanceId = null;
        if (null == workorderNum) {
            throw new UnitedException(BizcoreActivitiConstants.WORK_ORDER_EXIST);
        }
        final WorkOrder model = this.workOrderRepository.findByWorkOrderNum(workorderNum);

        if (null == model) {
            final ProcessInstance processInstance = this.runtimeService.createProcessInstanceQuery()
                    .processInstanceBusinessKey(workorderNum).singleResult();
            if (null == processInstance) {
                throw new UnitedException(BizcoreActivitiConstants.WORK_ORDER_EXIST);
            }
            instanceId = processInstance.getProcessInstanceId();
        } else {
            instanceId = model.getInstanceId();
        }
        return instanceId;
    }

    @Override
    public String getWorkorderNumByInstanceId(final String instanceId) {

        if (null == instanceId) {
            throw new UnitedException(BizcoreActivitiConstants.PROCESS_EXIST);
        }
        final WorkOrder model = this.workOrderRepository.findByInstanceId(instanceId);

        if (null == model) {
            throw new UnitedException(BizcoreActivitiConstants.WORK_ORDER_EXIST);
        }

        return model.getWorkOrderNum();
    }

    @Override
    public String temporaryStorage(final String title, final String type, final String workorderNum,
            final UserBean user) {

        final WorkorderDTO dto =
                WorkorderDTO.builder().applyUserId(user.getUsername()).applyUserName(user.getName()).title(title)
                        .type(type).workOrderNum(workorderNum).status(WorkOrderStatus.temporary).build();

        if (!StringUtils.isEmpty(workorderNum)) {
            final WorkOrder model = this.workOrderRepository.findByWorkOrderNum(workorderNum);
            if (model != null) {
                dto.setId(model.getId());
                dto.setInstanceId(model.getInstanceId());
                dto.setApplyUserId(model.getApplyUserId());
                dto.setApplyUserName(model.getApplyUserName());
            } else {
                dto.setApplyUserId(user.getUsername());
                dto.setApplyUserName(user.getName());
            }

        } else {
            dto.setWorkOrderNum(this.serialNumService
                    .generateSerialNumberByModelCode(BizcoreActivitiConstants.WORK_ORDER_MODEL_CODE_KEY));
            dto.setApplyUserId(user.getUsername());
            dto.setApplyUserName(user.getName());
        }

        final WorkOrder model = this.workOrderConverter.toModel(dto);
        this.workOrderRepository.save(model);

        return model.getWorkOrderNum();
    }

    @Override
    public ProcessInstanceDTO submit(final String title, final String type, final String workorderNum,
            final UserBean user) {

        // 构建工单
        final WorkorderDTO dto =
                WorkorderDTO.builder().applyUserId(user.getUsername()).applyUserName(user.getName()).title(title)
                        .type(type).workOrderNum(workorderNum).status(WorkOrderStatus.running).build();

        String instanceId = null;

        if (!StringUtils.isEmpty(workorderNum) && !"NULL".contentEquals(workorderNum)) {
            final WorkOrder model = this.workOrderRepository.findByWorkOrderNum(workorderNum);
            // 含有的情况下更新工单
            if (model != null) {
                dto.setId(model.getId());
                instanceId = model.getInstanceId();
            }
        } else {
            dto.setWorkOrderNum(this.serialNumService
                    .generateSerialNumberByModelCode(BizcoreActivitiConstants.WORK_ORDER_MODEL_CODE_KEY));
        }

        if (type == null) {
            throw new UnitedException(BizcoreActivitiConstants.PROCESS_EXIST);
        }

        ProcessInstanceDTO pi;

        if (StringUtils.isEmpty(instanceId)) {
            // 启动流程
            pi = this.processInstanceService.start(type, dto.getWorkOrderNum(), user);
            // 设置工单的实例ID
            instanceId = pi.getInstanceId();
        } else {
            dto.setInstanceId(instanceId);
            final List<Task> currentTask = this.taskService.createTaskQuery().processInstanceId(instanceId).list();
            pi = ProcessInstanceDTO.builder().instanceId(instanceId).workorderNum(workorderNum)
                    .taskId(CollectionUtils.isEmpty(currentTask) ? "" : currentTask.get(0).getId()).build();

        }

        // 设置实例
        dto.setInstanceId(instanceId);
        // 保存工单
        this.workOrderRepository.save(this.workOrderConverter.toModel(dto));

        return pi;
    }

    @Override
    public void updateStatus(final String workorderNum, final WorkOrderStatus status) {

        final WorkOrder model = this.workOrderRepository.findByWorkOrderNum(workorderNum);
        if (null == model) {
            throw new UnitedException(BizcoreActivitiConstants.WORK_ORDER_EXIST);
        }

        model.setStatus(status);
        this.workOrderRepository.save(model);
    }

    private Page<WorkorderDTO> workSearch(final Pageable pageable, final Searchable searchable, final String type,
            final UserBean user) {

        if (null == user) {
            throw new UnitedException(BizcoreActivitiConstants.PROCESS_USER_EXIST);
        }

        List<TaskDTO> tasks;

        switch (type) {
        case BizcoreActivitiConstants.TASK_TYPR_TODO:
            tasks = this.processInstanceService.getTodoTask(user);
            final List<String> ids = tasks.stream().map(k -> k.getInstanceId()).distinct().collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ids)) {
                ids.add("NAN");
            }
            searchable.put("instanceIds", ids);
            break;
        case BizcoreActivitiConstants.TASK_TYPR_DONE:
            tasks = this.processInstanceService.getDoneTask(user);
            final List<String> instancesIds =
                    tasks.stream().map(k -> k.getInstanceId()).distinct().collect(Collectors.toList());
            if (CollectionUtils.isEmpty(instancesIds)) {
                instancesIds.add("NAN");
            }
            searchable.put("instanceIds", instancesIds);
            break;
        case BizcoreActivitiConstants.TASK_TYPR_ME:
            searchable.put("userName", user.getUsername());
            break;
        default:
            break;
        }

        final Page<WorkOrder> models = this.workOrderRepository.search(pageable, searchable);

        final Page<WorkorderDTO> result = this.workOrderConverter.toPageDTO(models);

        result.getContent().forEach(k -> {
            this.addProceeInfo(k, user);
        });

        return result;
    }

    private long countWorkSearch(final Searchable searchable, final String type,
            final UserBean user) {

        if (null == user) {
            throw new UnitedException(BizcoreActivitiConstants.PROCESS_USER_EXIST);
        }

        List<TaskDTO> tasks;

        switch (type) {
        case BizcoreActivitiConstants.TASK_TYPR_TODO:
            tasks = this.processInstanceService.getTodoTask(user);
            final List<String> ids = tasks.stream().map(k -> k.getInstanceId()).distinct().collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ids)) {
                ids.add("NAN");
            }
            searchable.put("instanceIds", ids);
            break;
        case BizcoreActivitiConstants.TASK_TYPR_DONE:
            tasks = this.processInstanceService.getDoneTask(user);
            final List<String> instancesIds =
                    tasks.stream().map(k -> k.getInstanceId()).distinct().collect(Collectors.toList());
            if (CollectionUtils.isEmpty(instancesIds)) {
                instancesIds.add("NAN");
            }
            searchable.put("instanceIds", instancesIds);
            break;
        case BizcoreActivitiConstants.TASK_TYPR_ME:
            searchable.put("userName", user.getUsername());
            break;
        default:
            break;
        }

        return this.workOrderRepository.count(searchable);
    }

    private void addProceeInfo(final WorkorderDTO dto, final UserBean user) {

        final String instanceId = dto.getInstanceId();
        if (StringUtils.isNotEmpty(instanceId)) {

            final List<Task> tasks = this.taskService.createTaskQuery().processInstanceId(instanceId)
                    .list();

            if (CollectionUtils.isNotEmpty(tasks)) {

                // 设置表单节点
                dto.setCurrentNode(this.processFormService.getFormNameBykey(tasks.get(0).getFormKey()));

                final List<String> assigneeList = new ArrayList<>();

                tasks.stream().forEach(k -> {
                    final List<IdentityLink> links = this.taskService.getIdentityLinksForTask(k.getId());
                    if (CollectionUtils.isNotEmpty(links)) {
                        links.stream().map(s -> {
                            String approve = "";
                            switch (s.getType()) {
                            case BizcoreActivitiConstants.APPROVE_GROUP_KEY:
                                if (StringUtils.isNotEmpty(s.getGroupId())) {
                                    final Map<String, Object> approveRole =
                                            this.userDetail.getRoleInfo(s.getGroupId());
                                    if ((null != approveRole) && approveRole.containsKey("name")) {
                                        approve = approveRole.get("name").toString();
                                    }
                                }

                                if (StringUtils.isNotEmpty(s.getUserId())) {
                                    final UserBean approveUser = this.userDetail.getUserInfo(s.getUserId());
                                    if (null != approveUser) {
                                        approve = approveUser.getName();
                                    }
                                }

                                break;
                            case BizcoreActivitiConstants.APPROVE_USER_KEY:
                                if (StringUtils.isNotEmpty(s.getUserId())) {
                                    final UserBean approveUser = this.userDetail.getUserInfo(s.getUserId());
                                    if (null != approveUser) {
                                        approve = approveUser.getName();
                                    }
                                }
                                break;
                            case BizcoreActivitiConstants.APPROVE_ASSIGNEE_KEY:
                                if (StringUtils.isNotEmpty(s.getUserId())) {
                                    final UserBean approveUser = this.userDetail.getUserInfo(s.getUserId());
                                    if (null != approveUser) {
                                        approve = approveUser.getName();
                                    }
                                }
                                break;
                            default:
                                break;
                            }
                            return approve;
                        }).filter(m -> StringUtils.isNotEmpty(m)).forEach(assigneeList::add);
                    }

                });

                // 设置审批人/角色
                if (CollectionUtils.isNotEmpty(assigneeList)) {
                    final String currentNodeUser = assigneeList.stream().distinct()
                            .map(String::valueOf).collect(Collectors.joining(","));
                    dto.setCurrentNodeUser(currentNodeUser);
                }

            } else {
                dto.setCurrentNode(BizcoreActivitiConstants.PROCEE_END_NODE_NAME);
            }

        } else {
            // 显示初始化表单
            final ProcessNodeDTO node = this.definitionService.getStartForm(dto.getType());
            dto.setCurrentNode(this.processFormService.getFormNameBykey(node.getFormKey()));
            dto.setCurrentNodeUser(dto.getApplyUserName());
        }

        final List<String> optionBtn = new ArrayList<>();

        if (null != user) {

            final String userId = user.getUsername();
            // 角色
            final List<String> roles = user.getRoles().stream().map(s -> s.getRole()).collect(Collectors.toList());

            // 处理按钮 当前节点处理人/角色是当前用户 && 实例ID不为空
            if (StringUtils.isNotEmpty(instanceId)) {

                final List<Task> checkTask = this.taskService.createTaskQuery().processInstanceId(instanceId).or()
                        .taskCandidateOrAssigned(userId).taskCandidateGroupIn(roles)
                        .endOr().list();

                // 登录用户是否有权限审批
                if (CollectionUtils.isNotEmpty(checkTask)) {
                    optionBtn.add(BizcoreActivitiConstants.HANDLE_BTN_KEY);
                }

            }
            // 删除按钮 编辑按钮  申请人是当前用户 && 并且实例ID为空
            if (userId.equals(dto.getApplyUserId()) && StringUtils.isEmpty(instanceId)) {
                optionBtn.add(BizcoreActivitiConstants.DELETE_BTN_KEY);
                optionBtn.add(BizcoreActivitiConstants.EDIT_BTN_KEY);
            }

            // 撤销按钮 申请人是当前用户
            if (userId.equals(dto.getApplyUserId()) && StringUtils.isNotEmpty(instanceId)
                    && !(dto.getStatus().equals(WorkOrderStatus.revoked)
                            || dto.getStatus().equals(WorkOrderStatus.finish))) {
                optionBtn.add(BizcoreActivitiConstants.REVOKE_BTN_KEY);
            }

            // 查看按钮 申请人是当前用户 || 其他节点处理人是当前用户
            if (userId.equals(dto.getApplyUserId())) {
                optionBtn.add(BizcoreActivitiConstants.VIEW_BTN_KEY);
            }
            if (StringUtils.isNotEmpty(instanceId)) {
                final List<HistoricTaskInstance> checkList = this.historyService.createHistoricTaskInstanceQuery()
                        .processInstanceId(instanceId).taskAssignee(userId)
                        .list();
                if (CollectionUtils.isNotEmpty(checkList)) {
                    optionBtn.add(BizcoreActivitiConstants.VIEW_BTN_KEY);
                }
            }
        }

        // 设置按钮
        dto.setOptionBtns(optionBtn);
    }

    @Override
    public void updateTime(final String workorderNum) {
        final WorkOrder model = this.workOrderRepository.findByWorkOrderNum(workorderNum);
        if (null != model) {
            model.setUpdateTime(new Date());
            this.workOrderRepository.save(model);
        }

    }

}
